(PYTHON) Day - 22 Regex and Parsing(2)

Reference

  • 문제 출처 - HackerRank
  • 파이썬 연습 - Practice - Python

개인적인 생각과 상상으로 작성한 내용들이 포함되어 있습니다
문제를 풀고 Discussion Tab을 참고하며 코드 스타일을 개선하려고 노력하고자 합니다


HackerRank


Regex and Parsing


Detect Floating Point Number


문제 : 입력이 형식에 맞는지 확인하는 문제
입력 : 입력받을 개수 N; (N 반복) 문자열;
출력 : 각 문자열이 형식에 맞는지 True/False 로 출력
형식 :

  1. 숫자는 +/-/. 으로 시작 할 수 있다
  2. 숫자는 최소 1개의 소수점 아래 값을 가진다(즉, 정수가 아님)
  3. float() 타입으로 변환할 때 오류가 없어야 한다

4
4.0O0
-1.00
+4.54
SomeRandomStuff

False
True
True
False

정규표현식 기호를 잘 외우자

'''
^: 시작을 나타냄
[+-]?: (`+` 혹은 `-`) 둘 중 하나가 있거나 없을 수 있다
\d\*: 숫자가 있을 수도 있고 없을 수도 있다 <- \d는 [0-9]로 표현할 수도 있다
\.: `.` 문자
\d+: 적어도 하나의 숫자가 있다
$: 끝을 나타냄
'''

import re

for \_ in range(int(input())):
n = input()
print(bool(re.search(r'^[+-]?\d*\.\d+$', n))) # print(bool(re.search(r'^[+-]?[0-9]*\.[0-9]+$', n)))

Re.split()


문제 : 쉼표나 점을 구분하는 정규표현식

100,000,000.000

100
000
000
000

그냥 내장함수로도 구현할 수 있지 않나?

print(\*input().replace(',', '.').split('.'), sep='\n') 

어쨌든 정규표현식을 사용하면 다음과 같다

regex_pattern = r'[\.\,]'

import re
print("\n".join(re.split(regex_pattern, input())))

Group(), Groups() & Groupdict()


문제 : 처음으로 연속 반복되는 영문자 혹은 숫자를 출력하는 문제
입력 : 문자열
출력 : 반복되는 첫 영숫자(없다면 -1 출력)
예제 : .은 반복되지만 영숫자(alphanumeric)가 아님, 111이 반복되므로 답은 1

..12345678910111213141516171820212223

1

3가지 표현이 가능함

import re

# S = re.search(r"([A-Za-z0-9])\1+", input())

# S = re.search(r"(\w(?!\_))\1+", input())

S = re.search(r"([^\w_])\1+", input())

# print(S[1] if S else -1)

print(S.group(1) if S else -1)

Re.findall() & Re.finditer()


문제 : 문자열에서 양끝이 자음(consonant)이면서 2개의 모음(vowel)을 포함하는 서브 문자열을 찾는 문제
입력 : 공백, +, - 를 포함하는 문자열
출력 : 조건을 만족하는 문자열

rabcdeefgyYhFjkIoomnpOeorteeeeet

ee
Ioo
Oeo
eeeee

모범 답안
조건이 있는 표현식(?<= 와 같은) 을 잘 숙지할 것
컴파일 옵션으로 re.IGNORECASE 를 사용하여 대소문자를 구분하지 않아도 됨

import re

VOWELS = 'aeiou'
CONSONANTS = 'bcdfghjklmnpqrstvwxyz'
REGEX = '(?<=[' + CONSONANTS + '])([' + VOWELS + ']{2,})[' + CONSONANTS + ']'

match = re.findall(REGEX, input(), re.IGNORECASE)
if match:
print(\*match, sep='\n')
else:
print('-1')

Re.start() & Re.end()


문제 : 찾는 문자열의 위치(범위)를 출력하는 문제
입력 : 문자열 S; 찾고자 하는 문자열 k;
출력 : S에서 k문자열의 위치들

aaadaa
aa

(0, 1)
(1, 2)
(4, 5)

조금 비효율적인 방법인 것 같다ㅠ

import re

S = input()
k = input()
pattern = re.compile(k)
searched = pattern.search(S)

if searched is None:
print(-1, -1)
else:
for i in range(len(S)):
if pattern.match(S[i:]):
print((i,i+len(k)-1))

조금 정리한 버전

import re

S = input()
k = input()

pattern = re.compile(k)
r = pattern.search(S)

if not r:
print((-1, -1))
while r:
print("({0}, {1})".format(r.start(), r.end() - 1))
r = pattern.search(S, r.start() + 1)

Regex Substitution


문제 : 기호를 문자로 바꾸는 문제 (&& → and, || → or)
입력 : 입력줄 수 N; (N 반복) 문자열;
출력 : 바뀐 문자열

11
a = 1;
b = input();

if a + b > 0 && a - b < 0:
start()
elif a*b > 10 || a/b < 1:
stop()
print set(list(a)) | set(list(b))

#Note do not change &&& or ||| or & or |

#Only change those ‘&&’ which have space on both sides.

#Only change those ‘|| which have space on both sides.

a = 1;
b = input();

if a + b > 0 and a - b < 0:
start()
elif a*b > 10 or a/b < 1:
stop()
print set(list(a)) | set(list(b))

#Note do not change &&& or ||| or & or |

#Only change those ‘&&’ which have space on both sides.

#Only change those ‘|| which have space on both sides.

해당 단원에서는 정규식 사용해서 푸는 것을 권장하지만, 이 문제는 굳이 사용하지 않아도 될 것 같다

for \_ in range(int(input())):
line = input()

while ' && ' in line or ' || ' in line:
line = line.replace(" && ", " and ").replace(" || ", " or ")

print(line)

앞뒤 빈칸을 신경써서 문제를 풀어야한다… 그냥 위 방식이 더 깔끔한 것 같다

import re

for \_ in range(int(input())):
S = input()
S = re.sub(r' &&(?= )', ' and', S)
S = re.sub(r' \|\|(?= )', ' or', S)
print(S)

Validating Roman Numerals


문제 : 로마숫자가 4000보다 작은지 확인하는 문제
입력 : 로마숫자
출력 : True/False

CDXXI

True

로마숫자 모듈이 존재한다는 것을 처음 알았다

from roman import fromRoman

try:
if 0<fromRoman(input())<4000:
print(True)
else:
print(False)
except:
print(False)

rf'' 이렇게도 작성이 가능하구나… 그냥 적어봤는데 에러가 안 나서 신기했다

thousand = 'M{0,3}'
hundred = '(C[MD]|D?C{0,3})'
ten = '(X[CL]|L?X{0,3})'
digit = '(I[VX]|V?I{0,3})'

regex_pattern = rf"{thousand}{hundred}{ten}{digit}$"

import re
print(str(bool(re.match(regex_pattern, input()))))

Validating phone numbers


문제 : 7,8,9 로 시작하며 10자리인 전화번호를 구분하는 문제
입력 : 번호 개수 N; (N 반복) 문자열;
출력 : Yes/No

2
9587456281
1252478965

YES
NO

import re

for \_ in range(int(input())):
line = input()
if re.match(r"^[789]{1}\d{9}$", line):
print("YES")
else:
print("NO")

Validating and Parsing Email Addresses


문제 : 양식에 맞는 이메일만 구별해서 출력
입력 : 이메일 개수 n; (n 반복) 이름, 이메일 주소;
출력 : name user@email.com 형식

2
DEXTER dexter@hotmail.com
VIRUS <virus!@variable.:p>

DEXTER dexter@hotmail.com

그냥 정규식만 사용

import re
pattern=r'(?<=<)[a-z][a-z0-9\.\_\-]*@[a-z]+\.[a-z]{1,3}(?=>)'
for \_ in range(int(input())):
s = input()
if bool(re.search(pattern,s,re.IGNORECASE)):
print(s)

email.utils 모듈 사용

import re
import email.utils
pattern=r'[a-z][a-z0-9\.\_\-]\*@[a-z]+\.[a-z]{1,3}$'
for \_ in range(int(input())):
tup=email.utils.parseaddr(input())
if bool(re.match(pattern,tup[1],re.IGNORECASE)):
print(email.utils.formataddr(tup))